﻿#region

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using CuongHanh.DAL;

#endregion

namespace CuongHanh.Context
{
    public class CacheStringKey<T>
            where T : CacheStringClass
    {
        public static CacheStringKey<T> Singleton = new CacheStringKey<T>();

        private readonly Dictionary<string, T> _cacheDatas = new Dictionary<string, T>();

        private readonly object _lockCache = new object();

        private bool NoInactive = false;

        public CacheStringKey()
        {
            GetNoInactive();
            LoadCache();
        }

        private void GetNoInactive()
        {
            Type notMappedAttribute = typeof (NotMappedAttribute);
            PropertyInfo inactive = null;
            foreach (PropertyInfo propertyInfo in typeof (T).GetProperties())
            {
                if(propertyInfo.Name != "Inactive")
                    continue;
                inactive = propertyInfo;
                break;
            }
            if(inactive == null)
                return;
            List<Type> customAttributes = inactive.CustomAttributes.Select(c => c.AttributeType).ToList();
            foreach (Type type in customAttributes)
            {
                if(notMappedAttribute != type)
                    continue;
                NoInactive = true;
                return;
            }
        }

        public void LoadCache()
        {
            lock (_lockCache)
            {
                using (UnitOfWork unitOfWork = new UnitOfWork())
                {
                    IQueryable<T> models = NoInactive
                            ? unitOfWork.Where<T>()
                            : unitOfWork.Where<T>(c => !c.Inactive);
                    foreach (T model in models)
                        _cacheDatas[model.Key] = model;
                }
            }
        }

        public void UpdateCached(T model)
        {
            lock (_lockCache)
            {
                if(string.IsNullOrWhiteSpace(model.Key))
                    return;

                if(!NoInactive
                   && model.Inactive
                   && _cacheDatas.ContainsKey(model.Key))
                    _cacheDatas.Remove(model.Key);
                else
                    _cacheDatas[model.Key] = model;
            }
        }

        /// <summary>
        ///     chi su dung cho truong hop NoInactive
        /// </summary>
        /// <param name="model"></param>
        public void DeleteCached(T model)
        {
            lock (_lockCache)
            {
                if(_cacheDatas.ContainsKey(model.Key))
                    _cacheDatas.Remove(model.Key);
            }
        }

        public IEnumerable<T> GetObjectsByFilter(Expression<Func<T, bool>> filter = null)
        {
            lock (_lockCache)
            {
                if(filter != null)
                {
                    return _cacheDatas.Values.AsQueryable().Where(filter);
                }
                return _cacheDatas.Values;
            }
        }

        public T GetObject(string key)
        {
            lock (_lockCache)
            {
                return _cacheDatas.ContainsKey(key)
                        ? _cacheDatas[key]
                        : null;
            }
        }

        public T FirstOrDefaultObject(Expression<Func<T, bool>> filter = null)
        {
            lock (_lockCache)
            {
                return filter != null
                        ? _cacheDatas.Values.AsQueryable().FirstOrDefault(filter)
                        : null;
            }
        }
    }
}

public class CacheStringClass
{
    public virtual string Key { get; set; }

    public virtual bool Inactive { get; set; }
}
